package net.cyue.easyconfiguration.node.data;

import net.cyue.easyconfiguration.node.NodeType;
import net.cyue.easyconfiguration.node.annotation.FieldNode;
import net.cyue.easyconfiguration.node.annotation.FieldNodeIgnore;
import net.cyue.easyconfiguration.node.api.INodeTypeConverter;
import net.cyue.easyconfiguration.node.converter.DefaultConverter;
import net.cyue.easyconfiguration.node.util.NodePathUtil;
import net.cyue.util.ReflectUtil;
import net.cyue.util.StringUtil;

import java.lang.reflect.Field;

public class NodeMetaData {
    private boolean ignore;
    private boolean multiNode; // 是否多节点，多节点则返回 Map
    private String name;
    private String[] paths;
    private NodeType type;
    private Class<?> javaType;
    private NodeType elementType;
    private Class<?> elementJavaType;
    private boolean forceConvert;
    private boolean elementForceConvert;
    private INodeTypeConverter typeConverter;
    private INodeTypeConverter elementTypeConverter;

    public NodeMetaData(String name, String... paths) {
        this(NodeType.ANY, name, paths);
    }
    public NodeMetaData(NodeType type, String name, String... paths) {
        this.ignore = false;
        this.multiNode = false;
        this.setName(name);
        this.setPaths(paths);
        this.setType(type);
        this.javaType = null;
        this.elementType = NodeType.ANY;
        this.elementJavaType = null;
        this.forceConvert = true;
        this.elementForceConvert = true;
        this.typeConverter = null;
        this.elementTypeConverter = null;
    }

    private static INodeTypeConverter createTypeConverter(
        Class<? extends INodeTypeConverter> clz
    ) {
        if (clz == null || clz == DefaultConverter.class) {
            return null;
        }
        try {
            return ReflectUtil.createInstance(clz);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
    public static NodeMetaData fromField(Field field) {
        FieldNode fieldNode = field.getAnnotation(FieldNode.class);
        FieldNodeIgnore fieldNodeIgnore = field.getAnnotation(FieldNodeIgnore.class);

        boolean ignore = false;
        boolean multiNode = false;
        String name;
        String[] paths;
        NodeType nodeType = NodeType.fromClass(field.getType());
        NodeType elementType = NodeType.ANY;
        Class<?> elementJavaType = null;
        boolean forceConvert = true;
        boolean elementForceConvert = true;
        INodeTypeConverter<?, ?> typeConverter = null;
        INodeTypeConverter<?, ?> elementTypeConverter = null;

        // 忽略的字段
        if (
            (fieldNode != null && fieldNode.ignore() )||
            fieldNodeIgnore != null
        ) {
            ignore = true;
        }

        if (fieldNode == null) {
            name = field.getName();
            paths = new String[]{name};
        } else {
            // multiNode
            multiNode = fieldNode.multiNode();

            // name
            if (StringUtil.isBlank(fieldNode.name())) {
                name = field.getName();
            } else {
                name = fieldNode.name();
            }
            // paths
            if (fieldNode.paths().length == 0) {
                paths = new String[]{name};
            } else {
                paths = fieldNode.paths();
            }
            // NodeType
            if (fieldNode.type() != NodeType.ANY) {
                nodeType = fieldNode.type();
            }


            // 特殊处理数组的 ElementType 与 ElementClass
            if (nodeType == NodeType.ARRAY) {
                Class<?> componentType = field.getType().getComponentType();
                if (componentType == null) {
                    componentType = Object.class;
                }

                elementType = fieldNode.elementType();
                elementJavaType = fieldNode.elementJavaType();
                if (elementType == NodeType.ANY) {
                    elementType = NodeType.fromClass(componentType);
                }
                if (elementJavaType == null || elementJavaType == Object.class) {
                    elementJavaType = componentType;
                }
            }

            // force convert
            forceConvert = fieldNode.forceConvert();
            elementForceConvert = fieldNode.elementForceConvert();
            // type converter
            typeConverter = createTypeConverter(fieldNode.typeConverter());
            elementTypeConverter = createTypeConverter(fieldNode.elementTypeConverter());
        }
        NodeMetaData metaData = new NodeMetaData(nodeType, name, paths);
        metaData.setIgnore(ignore);
        metaData.setMultiNode(multiNode);
        metaData.setJavaType(field.getType());
        metaData.setElementType(elementType);
        metaData.setElementJavaType(elementJavaType);
        metaData.setForceConvert(forceConvert);
        metaData.setElementForceConvert(elementForceConvert);
        metaData.setTypeConverter(typeConverter);
        metaData.setElementTypeConverter(elementTypeConverter);

        return metaData;
    }

    public void setIgnore(boolean ignore) {
        this.ignore = ignore;
    }
    public void setMultiNode(boolean multiNode) {
        this.multiNode = multiNode;
        // 存在通配符的，则强制多节点
        if (NodePathUtil.hasWildcardPath(this.paths)) {
            this.multiNode = true;
        }
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setPaths(String... paths) {
        if (paths == null || paths.length == 0) {
            this.paths = new String[]{this.name};
            return;
        }
        this.paths = paths;
        // 存在通配符的，则强制多节点
        if (NodePathUtil.hasWildcardPath(paths)) {
            this.multiNode = true;
        }
    }
    public void setType(NodeType type) {
        this.type = type;
    }
    public void setJavaType(Class<?> nodeClass) {
        this.javaType = nodeClass;
    }
    public void setElementType(NodeType elementType) {
        this.elementType = elementType;
    }
    public void setElementJavaType(Class<?> elementClass) {
        this.elementJavaType = elementClass;
    }
    public void setForceConvert(boolean forceConvert) {
        this.forceConvert = forceConvert;
    }
    public void setElementForceConvert(boolean elementForceConvert) {
        this.elementForceConvert = elementForceConvert;
    }
    public void setTypeConverter(INodeTypeConverter typeConverter) {
        this.typeConverter = typeConverter;
    }
    public void setElementTypeConverter(INodeTypeConverter elementTypeConverter) {
        this.elementTypeConverter = elementTypeConverter;
    }


    public boolean isIgnore() {
        return ignore;
    }
    public boolean isMultiNode() {
        return multiNode;
    }
    public boolean isForceConvert() {
        return forceConvert;
    }
    public boolean isElementForceConvert() {
        return elementForceConvert;
    }
    public String getName() {
        return name;
    }
    public String[] getPaths() {
        return paths;
    }
    public NodeType getType() {
        return type;
    }
    public NodeType getNodeType() {
        return type;
    }
    public Class<?> getJavaType() {
        return javaType;
    }
    public Class<?> getNodeJavaType() {
        return javaType;
    }
    public String getElementName() {
        return name + "-element";
    }
    public NodeType getElementType() {
        return elementType;
    }
    public Class<?> getElementJavaType() {
        return elementJavaType;
    }
    public INodeTypeConverter getElementTypeConverter() {
        return elementTypeConverter;
    }
    public INodeTypeConverter getTypeConverter() {
        return typeConverter;
    }
}
